﻿/*******************************************************************************
 * 作者: hao.wang
 * 时间: 2013-10-23 10:13:33
 * 描述说明:
 *     调用日志
 * 更改历史:
 *     
*******************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Diagnostics;
using System.Threading;
using System.ServiceModel;
using System.Reflection;

namespace ChinaScope.CSF.Common
{
    public class LogProvider
    {
        internal static string MachineIP { get; private set; }
        internal static string MachineName { get; private set; }        

        static LogProvider()
        {
            MachineIP = string.Join(" / ", 
                Dns.GetHostAddresses(Dns.GetHostName()).Where(d => d.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                .Select(a => a.ToString()).ToArray());
            MachineName = Environment.MachineName;
        }

        private static string GetClientTypeName()
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame[] stackFrames = stackTrace.GetFrames();
            StackFrame stackFrame = null;
            foreach (var item in stackFrames)
            {
                if (item.GetMethod().DeclaringType.Assembly != typeof(LogProvider).Assembly &&
                    item.GetMethod().DeclaringType.Assembly != typeof(Thread).Assembly &&
                    item.GetMethod().DeclaringType.Assembly != typeof(ChannelFactory).Assembly &&
                    !item.GetMethod().DeclaringType.IsInterface)
                {
                    stackFrame = item;
                    break;
                }
            }
            if (stackFrame != null)
            {
                MethodBase methodBase = stackFrame.GetMethod();
                return methodBase.DeclaringType.FullName;
            }
            return null;
        }

        #region CreateLog
        private static TAbstractLogInfo GetAbstractLogInfo<TAbstractLogInfo>(
            string requestIdentity, 
            string extraInfo) where TAbstractLogInfo : AbstractLogInfo, new()
        {
            var abstractLogInfo = new TAbstractLogInfo()
            {
                ID = System.Guid.NewGuid().ToString(),
                MachineIP = MachineIP,
                MachineName = MachineName,
                Time = DateTime.Now,
                RequestIdentity = requestIdentity,
                ExtraInfo = extraInfo
            };
            return abstractLogInfo;
        }

        private static TMessage GetMessageInfo<TMessage>(
            string requestIdentity,
            string extraInfo,
            MessageDirection direction,
            string message) where TMessage : MessageInfo, new()
        {
            var log = GetAbstractLogInfo<TMessage>(requestIdentity, extraInfo);
            log.Message = message;
            log.MessageDirection = direction;
            return log;
        }

        private static TStartInfo GetStartInfo<TStartInfo>(
            string extraInfo)
            where TStartInfo : StartInfo, new()
        {
            var log = GetAbstractLogInfo<TStartInfo>(string.Empty, extraInfo);
            return log;
        }

        private static TInvokeInfo GetInvokeInfo<TInvokeInfo>(
            string extraInfo,
            long executionTime,
            bool isSuccessful,
            string methodName,
            ApplicationContext context)
            where TInvokeInfo : InvokeInfo, new()
        {
            var log = GetAbstractLogInfo<TInvokeInfo>(context == null ? string.Empty : context.RequestIdentity, extraInfo);
            log.ExecutionTime = executionTime;
            log.IsSuccessful = isSuccessful;
            log.MethodName = methodName;
            log.ApplicationContext = context;
            return log;
        }

        private static TExceptionInfo GetExceptionInfo<TExceptionInfo>(
            string requestIdentity,
            string extraInfo,
            Exception exception)
            where TExceptionInfo : ExceptionInfo, new()
        {
            var log = GetAbstractLogInfo<TExceptionInfo>(requestIdentity, extraInfo);
            log.Type = exception.GetType().FullName;
            log.Message = exception.Message;
            log.StackTrace = exception.StackTrace;
            return log;
        }

        #endregion

        #region StartInfo
        public static ServerStartInfo GetServerStartInfo(string serviceName,string extraInfo)
        {
            var log = GetStartInfo<ServerStartInfo>(extraInfo);
            log.ServiceName = serviceName;
            return log;
        }
        public static ClientStartInfo GetClientStartInfo(string contractName, string extraInfo)
        {
            var log = GetStartInfo<ClientStartInfo>(extraInfo);
            log.ContractName = contractName;
            log.ClientTypeName = GetClientTypeName();
            return log;
        }
        #endregion

        #region InvokeInfo
        public static ServerInvokeInfo GetServerInvokeInfo(
            string extraInfo, 
            long executionTime, 
            bool isSuccessful, 
            string methodName,
            ApplicationContext context,
            List<string> parameters, 
            List<string> results)
        {
            var log = GetInvokeInfo<ServerInvokeInfo>(extraInfo, executionTime, isSuccessful, methodName, context);
            log.ServiceName = OperationContext.Current.Host.Description.ServiceType.FullName;
            log.Parameters = parameters;
            log.Results = results;
            return log;
        }

        public static ClientInvokeInfo GetClientInvokeInfo(
            string contractName,
            string extraInfo,
            long executionTime,
            bool isSuccessful,
            string methodName,
            ApplicationContext context)
        {
            var log = GetInvokeInfo<ClientInvokeInfo>(extraInfo, executionTime, isSuccessful, methodName, context);
            log.ContractName = contractName;
            log.ClientTypeName = GetClientTypeName();
            return log;
        }
        #endregion

        #region ExceptionInfo
        public static ServerExceptionInfo GetServerExceptionInfo(
            string extraInfo,
            Exception exception)
        {
            var log = GetExceptionInfo<ServerExceptionInfo>(ServerApplicationContext.Current.RequestIdentity, extraInfo, exception);
            log.ServiceName = OperationContext.Current.Host.Description.ServiceType.FullName;
            log.InnerException = exception.InnerException == null ? string.Empty : exception.InnerException.ToString();
            return log;
        }

        public static ClientExceptionInfo GetClientExceptionInfo(
            string contractName,
            string requestIdentity,
            string extraInfo,
            Exception exception,
            string serverExceptionID)
        {
            var log = GetExceptionInfo<ClientExceptionInfo>(requestIdentity, extraInfo, exception);
            log.ContractName = contractName;
            log.ServerExceptionID = serverExceptionID;
            log.ClientTypeName = GetClientTypeName();
            return log;
        }
        #endregion

        #region MessageInfo
        public static ServerMessageInfo GetServerMessageInfo(
            string extraInfo, MessageDirection direction, string message)
        {
            var log = GetMessageInfo<ServerMessageInfo>(ServerApplicationContext.Current.RequestIdentity, extraInfo, direction, message);
            log.ServiceName = OperationContext.Current.Host.Description.ServiceType.FullName;
            return log;
        }

        public static ClientMessageInfo GetClientMessageInfo(
            string contractName,
            string requestIdentity,
            string extraInfo,
            MessageDirection direction,
            string message)
        {
            var log = GetMessageInfo<ClientMessageInfo>(requestIdentity, extraInfo, direction, message);
            log.ContractName = contractName;
            log.ClientTypeName = GetClientTypeName();
            return log;
        }
        #endregion

        static Dictionary<string, ILogService> dictLocal = new Dictionary<string, ILogService>();
        /// <summary>
        /// 获取本地日志服务
        /// </summary>
        /// <param name="logName">日志名称${logger} 为调用类typeof(T).FullName</param>
        /// <returns></returns>
        internal static ILogService GetLocalLogServer(string logName)
        {
            if (dictLocal.ContainsKey(logName))
            {
                return dictLocal[logName];
            }
            ILogService logService = new LocalLogService(logName);
            dictLocal.Add(logName, logService);
            return logService;
        }
    }
}
